/*
 * Copyright 2010 J Patrick Davenport
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package v4view.web.ext;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import v4view.core.utils.SimpleBinder;
import v4view.web.IPageElementCallback;
import v4view.web.Option;
import v4view.web.Select;

/**
 * A Select box that provides simple type binding. For example, if
 * you passed in a List<Person> and a Person has an getId and getFullName method,
 * you could set the displayMethod = "fullName" and valueMethod = "id" and a list
 * of options will bind to that. As an added feature, both displayMethod and valueMethod
 * provide in line concatenation. This means that you can specify "firstName + lastName"
 * and have it render as "Patrick Davenport". The + sign, inside the string, is converted
 * to a single space.
 *  
 * @author J Patrick Davenport
 *
 */
public class BindingSelect extends Select {
	private Collection<Object>	options;
	private String				valueMethod;
	private String				displayMethod;
	private String				defaultDisplay;
	private String				defaultValue;
	private String				selectedValue;
	private SimpleBinder		binder;

	{
		this.addLoadCallBack(new IPageElementCallback<BindingSelect>() {
			@Override
			public void execute(final BindingSelect _elementBeingLoaded) {
				BindingSelect.this.loadOptions(_elementBeingLoaded);
			}
		});

		this.binder = new SimpleBinder();
	}

	public String getDefaultDisplay() {
		return this.defaultDisplay;
	}

	public String getDefaultValue() {
		return this.defaultValue;
	}

	public String getDisplayMethod() {
		return this.displayMethod;
	}

	public Collection<Object> getOptions() {
		return this.options;
	}

	public String getValueMethod() {
		return this.valueMethod;
	}

	public BindingSelect setDefaultDisplay(final String _defaultDisplay) {
		this.defaultDisplay = _defaultDisplay;
		return this;
	}

	public BindingSelect setDefaultValue(final String _defaultValue) {
		this.defaultValue = _defaultValue;
		return this;
	}

	public BindingSelect setDisplayMethod(final String _displayMethod) {
		this.displayMethod = _displayMethod;
		return this;
	}

	@SuppressWarnings("unchecked")
	public BindingSelect setOptions(@SuppressWarnings("rawtypes") final List _options) {
		this.options = _options;
		return this;
	}

	public BindingSelect setValueMethod(final String _valueMethod) {
		this.valueMethod = _valueMethod;
		return this;
	}

	private void prepOption(final Option defaultOption) {
		this.addPageElement(defaultOption);
		defaultOption.setRequest(this.getRequest());
		defaultOption.setResponse(this.getResponse());
		defaultOption.load();
	}

	private void bindDisplay(final Object o, final Option option) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		option.setDisplay(this.binder.bind(o, this.displayMethod));
	}

	private void bindValue(final Object o, final Option option) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		option.setValue(this.binder.bind(o, this.valueMethod));
	}

	private void loadOptions(final BindingSelect loader) {
		if (CollectionUtils.isNotEmpty(this.options)
				&& StringUtils.isEmpty(this.displayMethod)
				&& StringUtils.isEmpty(this.valueMethod)) { throw new IllegalStateException("You must specify both displayMethod and valueMethod if you are to add automate option creation"); }
		if (StringUtils.isNotEmpty(this.defaultDisplay) && this.defaultValue != null) {
			final Option defaultOption = new Option();
			defaultOption.setValue(this.defaultValue);
			defaultOption.setDisplay(this.defaultDisplay);
			this.prepOption(defaultOption);
		}

		if (CollectionUtils.isNotEmpty(this.options)) {
			try {
				for (final Object o : this.options) {
					final Option option = new Option();
					this.bindDisplay(o, option);
					this.bindValue(o, option);
					this.prepOption(option);
					if (this.selectedValue != null && this.selectedValue.equals(option.getValue())) {
						option.setSelected(true);
					}
				}
			} catch (final Exception ex) {
				throw new IllegalArgumentException("Property was not valid", ex);
			}
		}
	}

	public String getSelectedValue() {
		return this.selectedValue;
	}

	public void setSelectedValue(final String _selectedValue) {
		this.selectedValue = _selectedValue;
	}
}
